using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Data.SqlClient;

namespace Code_Generator
{
	class MetaData
	{
		public readonly DBColumnList Columns = new DBColumnList();
		public DBColumn EnumDescriptionColumn;

		/// <summary>
		/// This will return the Primary Column. If there are no primary columns or more than one, this will throw an exception.
		/// </summary>
		public DBColumn PrimaryColumn
		{
			get { return (from c in Columns where c.IsPrimaryKey select c).Single(); }
		}

		public IEnumerable<DBColumn> PrimaryColumns
		{
			get { return (from c in Columns where c.IsPrimaryKey select c).ToList(); }
		}

		/// <summary>
		/// Loads the metadata as per the setup object.
		/// </summary>
		public static MetaData PopulateMetaData(Setup setup)
		{
			var ds = new DataSet();

			using (var con = setup.OpenConnection())
			{
				using (var cmd = new SqlCommand("sp_help", con))
				{
					cmd.CommandType = CommandType.StoredProcedure;
					cmd.Parameters.AddWithValue("@objname", setup.TableName);

					using (var reader = cmd.ExecuteReader())
					{
						ds.Load(reader, default(LoadOption), "Table", "Columns", "Identity", "Garbage", "Location", "Index", "Constraints", "References");
					}
				}
			}

			var x = new MetaData();

			var identityColumns = new List<String>();
			foreach (DataRow row in ds.Tables["Identity"].Rows)
			{
				identityColumns.Add(((string)row["Identity"]));
			}

			var primaryColumns = new List<String>();
			foreach (DataRow row in ds.Tables["Index"].Rows)
			{
				if (((string)row["index_description"]).Contains("primary key"))
				{
					//note, the marker (-) means the column is in descending order.
					primaryColumns.AddRange(((string)row["index_keys"]).Replace("(-)", "").Split(','));
				}
			}


			var columnsWithDefaults = new Dictionary<String, String>(StringComparer.OrdinalIgnoreCase);

			foreach (DataRow row in ds.Tables["Constraints"].Rows)
			{
				if (((string)row["constraint_type"]).StartsWith("DEFAULT on column "))
				{
					var columnName = ((string)row["constraint_type"]).Substring("DEFAULT on column ".Length);
					columnsWithDefaults.Add(columnName, ((string)row["constraint_keys"]));
				}
			}

			foreach (DataRow row in ds.Tables["Columns"].Rows)
			{
				var item = new DBColumn()
				{
					SqlName = ((string)row["Column_Name"]),
					SqlType = ((string)row["Type"]),
					Length = ((int)row["Length"]),
					IsNullable = ((string)row["Nullable"]) == "yes",
					IsComputed = ((string)row["Computed"]) == "yes",
					Precision = ((string)row["Prec"]).Trim(),
					Scale = ((string)row["Scale"]).Trim()
				};

				item.IsIdentity = identityColumns.Contains(item.SqlName);

				item.IsPrimaryKey = primaryColumns.Contains(item.SqlName);

				if (columnsWithDefaults.ContainsKey(item.SqlName))
					item.SqlDefault = columnsWithDefaults[item.SqlName];

				if (setup.TokenFields.Any(c => String.Equals(c, item.SqlName, StringComparison.OrdinalIgnoreCase)))
					item.IsTokenColumn = true;


				if (String.Equals(setup.CreatedByField, item.SqlName, StringComparison.OrdinalIgnoreCase))
					item.IsCreatedBy = true;

				if (String.Equals(setup.CreatedDateField, item.SqlName, StringComparison.OrdinalIgnoreCase))
					item.IsCreatedDate = true;

				if (String.Equals(setup.UpdatedByField, item.SqlName, StringComparison.OrdinalIgnoreCase))
					item.IsUpdatedBy = true;

				if (String.Equals(setup.UpdatedDateField, item.SqlName, StringComparison.OrdinalIgnoreCase))
					item.IsUpdatedDate = true;


				if (String.Equals(setup.EnumDescription, item.SqlName, StringComparison.OrdinalIgnoreCase))
					x.EnumDescriptionColumn = item;

				if (setup.EnumFieldTypeMap.ContainsKey(item.SqlName))
					item.ClrTypeOverride = setup.EnumFieldTypeMap[item.SqlName];

				x.Columns.Add(item);
			}

			if (!String.IsNullOrEmpty(setup.EnumDescription) && x.EnumDescriptionColumn == null)
				throw new Exception("Could not find column " + setup.EnumDescription);

			return x;
		}

		public DBColumn CreatedByColumn
		{
			get { return (from c in Columns where c.IsCreatedBy select c).FirstOrDefault(); }
		}
		public DBColumn CreatedDateColumn
		{
			get { return (from c in Columns where c.IsCreatedDate select c).FirstOrDefault(); }
		}
		public DBColumn UpdatedByColumn
		{
			get { return (from c in Columns where c.IsUpdatedBy select c).FirstOrDefault(); }
		}
		public DBColumn UpdatedDateColumn
		{
			get { return (from c in Columns where c.IsUpdatedDate select c).FirstOrDefault(); }
		}

	}
}
